#include "mat3.h"
#include "dmat3.h"

namespace libvec
{
  mat3::mat3()
  {
    vec3 v(0);

    this->v1 = v;
    this->v2 = v;
    this->v3 = v;
  }

  mat3::mat3(dmat3 m)
  {
    this->v1 = m.v1;
    this->v2 = m.v2;
    this->v3 = m.v3;
  }

  mat3::mat3(float value)
  {
    this->v1.x = value;
    this->v2.y = value;
    this->v3.z = value;
  }

  mat3::mat3(float n11, float n12, float n13,
      float n21, float n22, float n23,
      float n31, float n32, float n33)
  {
    this->v1.x = n11;
    this->v2.x = n12;
    this->v3.x = n13;

    this->v1.y = n21;
    this->v2.y = n22;
    this->v3.y = n23;

    this->v1.z = n31;
    this->v2.z = n32;
    this->v3.z = n33;
  }

  mat3::mat3(vec3 u, vec3 v, vec3 w)
  {
    this->v1 = u;
    this->v2 = v;
    this->v3 = w;
  }

  vec3 &mat3::operator[](int index)
  {
    static vec3 empty;

    switch (index)
    {
      case 0:
        return this->v1;

      case 1:
        return this->v2;

      case 2:
        return this->v3;
    }

    return empty;
  }

  mat3 mat3::operator=(mat3 m)
  {
    this->v1 = m.v1;
    this->v2 = m.v2;
    this->v3 = m.v3;

    return *this;
  }

  bool mat3::operator==(mat3 m)
  {
    return (this->v1 == m.v1) && (this->v2 == m.v2) && (this->v3 == m.v3);
  }

  bool mat3::operator!=(mat3 m)
  {
    return (this->v1 != m.v1) || (this->v2 != m.v2) || (this->v3 != m.v3);
  }

  mat3 &mat3::operator+()
  {
    return *this;
  }

  mat3 &mat3::operator-()
  {
    static mat3 out;

    out.v1 = -this->v1;
    out.v2 = -this->v2;
    out.v3 = -this->v3;

    return out;
  }

  mat3 mat3::operator+(mat3 m)
  {
    static mat3 out;

    out.v1 = this->v1 + m.v1;
    out.v2 = this->v2 + m.v2;
    out.v3 = this->v3 + m.v3;

    return out;
  }

  mat3 mat3::operator+=(mat3 m)
  {
    this->v1 += m.v1;
    this->v2 += m.v2;
    this->v3 += m.v3;

    return *this;
  }

  mat3 mat3::operator-(mat3 m)
  {
    static mat3 out;

    out.v1 = this->v1 - m.v1;
    out.v2 = this->v2 - m.v2;
    out.v3 = this->v3 - m.v3;

    return out;
  }

  mat3 mat3::operator-=(mat3 m)
  {
    this->v1 -= m.v1;
    this->v2 -= m.v2;
    this->v3 -= m.v3;

    return *this;
  }

  vec3 mat3::operator*(vec3 v)
  {
    static vec3 out;

    out.x = this->v1.x*v.x + this->v2.x*v.y + this->v3.x*v.z;
    out.y = this->v1.y*v.x + this->v2.y*v.y + this->v3.y*v.z;
    out.z = this->v1.z*v.x + this->v2.z*v.y + this->v3.z*v.z;

    return out;
  }

  mat3 mat3::operator*(mat3 m)
  {
    static mat3 out;

    out.v1.x = this->v1.x*m.v1.x + this->v2.x*m.v1.y + this->v3.x*m.v1.z;
    out.v2.x = this->v1.x*m.v2.x + this->v2.x*m.v2.y + this->v3.x*m.v2.z;
    out.v3.x = this->v1.x*m.v3.x + this->v2.x*m.v3.y + this->v3.x*m.v3.z;

    out.v1.y = this->v1.y*m.v1.x + this->v2.y*m.v1.y + this->v3.y*m.v1.z;
    out.v2.y = this->v1.y*m.v2.x + this->v2.y*m.v2.y + this->v3.y*m.v2.z;
    out.v3.y = this->v1.y*m.v3.x + this->v2.y*m.v3.y + this->v3.y*m.v3.z;

    out.v1.z = this->v1.z*m.v1.x + this->v2.z*m.v1.y + this->v3.z*m.v1.z;
    out.v2.z = this->v1.z*m.v2.x + this->v2.z*m.v2.y + this->v3.z*m.v2.z;
    out.v3.z = this->v1.z*m.v3.x + this->v2.z*m.v3.y + this->v3.z*m.v3.z;

    return out;
  }

  mat3 mat3::operator*=(mat3 m)
  {
    static mat3 out;

    out.v1.x = this->v1.x*m.v1.x + this->v2.x*m.v1.y + this->v3.x*m.v1.z;
    out.v2.x = this->v1.x*m.v2.x + this->v2.x*m.v2.y + this->v3.x*m.v2.z;
    out.v3.x = this->v1.x*m.v3.x + this->v2.x*m.v3.y + this->v3.x*m.v3.z;

    out.v1.y = this->v1.y*m.v1.x + this->v2.y*m.v1.y + this->v3.y*m.v1.z;
    out.v2.y = this->v1.y*m.v2.x + this->v2.y*m.v2.y + this->v3.y*m.v2.z;
    out.v3.y = this->v1.y*m.v3.x + this->v2.y*m.v3.y + this->v3.y*m.v3.z;

    out.v1.z = this->v1.z*m.v1.x + this->v2.z*m.v1.y + this->v3.z*m.v1.z;
    out.v2.z = this->v1.z*m.v2.x + this->v2.z*m.v2.y + this->v3.z*m.v2.z;
    out.v3.z = this->v1.z*m.v3.x + this->v2.z*m.v3.y + this->v3.z*m.v3.z;

    this->v1 = out.v1;
    this->v2 = out.v2;
    this->v3 = out.v3;

    return out;
  }

  mat3 mat3::operator*(float scale)
  {
    static mat3 out;

    out.v1 = this->v1 * scale;
    out.v2 = this->v2 * scale;
    out.v3 = this->v3 * scale;

    return out;
  }

  mat3 mat3::operator*=(float scale)
  {
    this->v1 *= scale;
    this->v2 *= scale;
    this->v3 *= scale;

    return *this;
  }

  mat3 mat3::operator/(float scale)
  {
    static mat3 out;

    out.v1 = this->v1 / scale;
    out.v2 = this->v2 / scale;
    out.v3 = this->v3 / scale;

    return out;
  }

  mat3 mat3::operator/=(float scale)
  {
    this->v1 /= scale;
    this->v2 /= scale;
    this->v3 /= scale;

    return *this;
  }
}

